package com.andy.note.main.single;

/**
 * 懒汉模式
 * 参考文章：
 * https://blog.csdn.net/qq_34490018/article/details/81608828
 * @author Andy
 *
 */
public class Singleton {
	/*
	 * 懒汉模式也就是懒加载模式，即延时加载模式，达到了Lazyloading（延时加载）的效果。
	 * 因为考虑到线程的安全性、延时加载和效率高低等问题，下面来看看懒汉模式的三种代码实现方式：
	 * 方式一：线程不安全，延时加载，效率高
	 * 此方式达到了延时加载，效率也比较高，但是只能在单线程下使用。
	 * 如果在多线程下使用，由于可能存在多个线程进入if代码块，创建多个实例，所以线程是不安全的。
	 * 多线程下不可用此方式。
	 */ 
//	private static Singleton singleton;
//
//	private Singleton() {
//	}
//
//	public static Singleton getInstance() {
//		if (singleton == null) {
//			singleton = new Singleton();
//		}
//		return singleton;
//	}


	/*
	 * 方式二：线程安全，延时加载，效率极低
	 * 此方式虽然使用同步方法加锁，解决了以上方式的线程不安全。
	 * 但是此方式效率极低，因为每个线程想获得实例的时候都要进行线程同步，无奈的等待和由于是单例，
	 * 那么其方法只执行过一次即可，所以造成效率低下。
	 */
//	private static Singleton singleton;
//
//	private Singleton() {
//	}
//
//	public static synchronized Singleton getInstance() {
//		if (singleton == null) {
//			singleton = new Singleton();
//		}
//		return singleton;
//	}
	
	/*
	 * 方式三：线程不安全，延时加载，效率较高
	 * 此方式使用了同步代码块的方式，先判断是否存在实例，如果不存在再执行同步代码块创建实例。
	 * 虽然这种方式采用先判断是否存在实例后再同步代码块，
	 * 使得创建了实例后就不再需要一直同步代码块了，其速度上会比使用同步方法快很多，但还是存在线程安全问题。
	 * 由于多线程情况下，可能存在多个线程同时进入if代码块再进行同步，同样会创建多个实例，造成线程不安全。
	 * 鉴于这些情况，人们又想到了解决方法，就是双重锁方式。不急，下面会说到！
	 */
//	private static Singleton singleton;
//
//	private Singleton() {
//	}
//
//	public static Singleton getInstance() {
//		if (singleton == null) {
//			synchronized (Singleton.class) {
//				singleton = new Singleton();
//			}
//		}
//		return singleton;
//	}

	/*
	 * 双重锁（懒汉式双重效验锁DCL）
	 * 静态内部类单例模式跟前面的饿汉模式机制类似，但又有细微的不同。
	 * 饿汉模式在类装载的时候就完成实例化了，虽然线程安全且效率高，但是没有达到延时加载。
	 * 而静态内部类方式在装载外类的时候并未完成初始化和实例化，而是调用静态工厂方法getInstance()才会装载内部类，
	 * 从而实现实例化，因此达到了我们想要的延时加载。
	 * 类的静态属性只会在第一次加载类的时候初始化，所以在这里，JVM帮助我们保证了线程的安全性，在类进行初始化时，
	 * 别的线程是无法进入的。所以这是推荐使用的单例模式实现方式。
	 */
	private static class SingletonHolder {
		private static Singleton instance = new Singleton();
	}

	private Singleton() {
		System.out.println("Singleton has loaded");
	}

	public static Singleton getInstance() {
		return SingletonHolder.instance;
	}
	
	public static void main(String[] args) {
		Singleton.getInstance();
	}
	
}
